package solutions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.pi4.locutil.GeoPosition;
import org.pi4.locutil.MACAddress;
import org.pi4.locutil.io.TraceGenerator;
import org.pi4.locutil.trace.TraceEntry;

import solutions.MainAlgorithmRunner.TraceEstimater;

public class ModelKNN extends BasePositioner implements TraceEstimater {
	public final static int METERS_IN_Y = 30;
	public final static int METERS_IN_X = 57;
	private static final double d_0 = 1;
	private static final double P_d0 = -33.77;
	private static final double N = 3.415;

	public static double GRID_SIZE = 1.5;

	public static int NR_GRIDS_X = (int) (METERS_IN_X / GRID_SIZE);
	public static int NR_GRIDS_Y = (int) (METERS_IN_Y / GRID_SIZE);

	/*
	 * modelbased uden WAF! P(d0) : strength at some reference distance d0 P(d)
	 * : strength at distance d from the antenna d : distace between AP and
	 * point of mesurement d0 : reference distance n : freespace loss
	 * 
	 * P(d)[dBm] = P(d0)[dBm] - 10nlog(d/d0);
	 * 
	 * 
	 * AP position kendt
	 */
	List<EntryWithMACsAndAverages> radioMap = new ArrayList<EntryWithMACsAndAverages>();
	private List<TraceEntry> apPositions;

	public ModelKNN(TraceGenerator tg, List<Double> differences, int k,
			List<TraceEntry> apPosition) {
		this.tg = tg;
		this.k = k;
		this.apPositions = apPosition;
		this.differences = differences;
	}

	public GeoPosition estimatePosition(TraceEntry onlineentry) {
		EntryWithMACsAndAverages onlineAverage = new EntryWithMACsAndAverages(
				onlineentry);
		for (EntryWithMACsAndAverages entry : radioMap) {
			double distance = 0;
			for (MACAddress address : onlineAverage.addresses.keySet()) {
				if (entry.addresses.containsKey(address)) {
					distance += PositionUtils.getDistance(
							onlineAverage.addresses.get(address),
							entry.addresses.get(address));
				}
			}
			entry.distance = distance;
		}

		Collections.sort(radioMap);

		double estimateX = 0, estimateY = 0, estimateZ = 0;
		List<EntryWithMACsAndAverages> entries = radioMap.subList(0, k);
		for (int i = 0; i < k; i++) {
			EntryWithMACsAndAverages entry = entries.get(i);
			GeoPosition position = entry.getPosition();
			estimateX = estimateX + position.getX();
			estimateY = estimateY + position.getY();
			estimateZ = estimateZ + position.getZ();
		}
		estimateX = estimateX / k;
		estimateY = estimateY / k;
		estimateZ = estimateZ / k;
		return new GeoPosition(estimateX, estimateY, estimateZ);
	}

	@Override
	public void buildRadioMap(List<TraceEntry> entries) {
		double x = 31.5, y = -18;

		for (int i = 0; i < NR_GRIDS_X; i++) {
			y = -18;
			for (int j = 0; j < NR_GRIDS_Y; j++) {
				GeoPosition gridPosition = new GeoPosition(x, y);

				Map<MACAddress, Double> wifiPositionsStrengths = new HashMap<MACAddress, Double>();

				for (TraceEntry entry : apPositions) {
					double distance = PositionUtils.getDistance(
							entry.getGeoPosition(), gridPosition);
					double P_d = P_d0 - 10 * N * Math.log10(distance / d_0);
					if (P_d <= 90)
						wifiPositionsStrengths.put(entry.getId(), P_d);
				}
				radioMap.add(new EntryWithMACsAndAverages(gridPosition,
						wifiPositionsStrengths));
				y += GRID_SIZE;
			}
			x -= GRID_SIZE;
		}
	}

	@Override
	public List<EntryWithMACsAndAverages> getRadioMap() {
		// TODO Auto-generated method stub
		return null;
	}
}
